import { Mesh } from '@babylonjs/core/Meshes/mesh';
import { COMPARE_COMMUNITY, ICommunityData } from '../../data/community';
import { DefaultStage } from '../default-stage';
import { HardLineGeometry } from 'babylon-lib/geometry/hard-line-geometry';
import { HardLineMaterial } from 'babylon-lib/material/hard-line-material';
import { Color3 } from '@babylonjs/core/Maths/math.color';
import { Vector3 } from '@babylonjs/core/Maths/math.vector';


class PathManager {

    private static _stage: DefaultStage;
    private static _pathMesh: Mesh;
    private static _pathVisible: boolean = true;

    public static readonly maxStep: number = 50;
    public static onGeneratePath: { (success: boolean): void } | null = null;

    public static initialize(stage: DefaultStage): void {
        this._stage = stage;
        this.initializePathMesh();
    }

    public static get displayPath(): boolean {
        return this._pathVisible;
    }

    public static set displayPath(value: boolean) {
        this._pathVisible = value;
        if (!this._pathVisible) this._pathMesh.isVisible = false;

    }

    public static computePath(pathPoints: ICommunityData[] = []): ICommunityData[] {
        const path: ICommunityData[] = [];
        for (let i = 0; i < pathPoints.length - 1; i++) {
            const from = pathPoints[i];
            const to = pathPoints[i + 1];
            if (i === 0) {
                const tp = this.targetPath(from, to);
                if (tp) {
                    tp.splice(tp.length - 1, 1);
                    path.push(...tp);
                }
            } else if (i === pathPoints.length - 2) {
                const tp = this.targetPath(from, to);
                if (tp) {
                    tp.splice(0, 1);
                    path.push(...tp);
                }
            } else {
                const tp = this.targetPath(from, to);
                if (tp) {
                    tp.splice(0, 1);
                    tp.splice(tp.length - 1, 1);
                    path.push(...tp);
                }
            }
        }

        if (this.displayPath) {
            const ps: Vector3[] = [];
            for (const d of path) {
                ps.push(d.location);
            }
            (this._pathMesh.geometry as HardLineGeometry).vertices = ps;
            this._pathMesh.isVisible = ps.length > 1;
        }
        if (this.onGeneratePath) {
            this.onGeneratePath(COMPARE_COMMUNITY(path[path.length - 1], pathPoints[pathPoints.length - 1]));
        }
        return path;
    }

    private static targetPath(from: ICommunityData, to: ICommunityData): ICommunityData[] | null {
        const paths: Array<ICommunityData[]> = [[from]];
        let count = 0;
        let result = -1;
        do {
            this.growPath(paths);
            for (let i = 0; i < paths.length; i++) {
                const p = paths[i];
                if (COMPARE_COMMUNITY(p[p.length - 1], to)) {
                    result = i;
                    break;
                }
            }
        } while (count < this.maxStep && result >= 0);
        if (result < 0) {
            return null;
        }
        return paths[result];
    }

    private static growPath(paths: Array<ICommunityData[]>) {
        const length = paths.length;
        for (let i = 0; i < length; i++) {
            const path = paths[i];
            const targets = path[path.length - 1].pathTo;
            if (!targets) {
                console.warn('the path was not configured');
                continue;
            }
            for (let j = 0; j < targets.length; j++) {
                const target = targets[j];
                if (j === 0) {
                    path.push(target);
                } else {
                    paths.push([...path, target]);
                }
            }
        }
        for (let i = 0; i < paths.length; i++) {
            const path = paths[i];
            const last = path[path.length - 1];
            let ext = false;
            for (let j = 0; j < path.length - 1; j++) {
                if (COMPARE_COMMUNITY(path[j], last)) {
                    ext = true;
                    break;
                }
            }
            if (ext) {
                paths.splice(i, 1);
                i--;
            }
        }
    }

    private static initializePathMesh(): void {
        const g = new HardLineGeometry('pathGeometry', [], {
            arrowMode: 1,
        }, this._stage.mainScene);
        const m = new HardLineMaterial('pathMaterial', this._stage.mainScene, {
            width: 0.04,
            diffuseColor: Color3.FromHexString('#ffffff'),
        });
        const mesh = new Mesh('pathMesh', this._stage.mainScene);
        g.applyToMesh(mesh);
        mesh.material = m;
        mesh.alwaysSelectAsActiveMesh = true;
        mesh.isPickable = false;
        mesh.isVisible = false;
        this._pathMesh = mesh;
    }
}


export { PathManager };